package com.yangyang.lamviarm.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.yangyang.lamviarm.util.Constants;

public class JPredicate {

	private String mName;
	private Object[] mParams;

	public String getName() {
		return this.mName;
	}

	public Object[] getParams() {
		return this.mParams;
	}

	public JPredicate(List<String> array) {
		this.mName = array.get(0);
		this.mParams = array.subList(1, array.size()).toArray();
	}

	public JPredicate(String array[]) {
		this.mName = array[0];
		this.mParams = new Object[array.length - 1];
		for (int i = 1; i < array.length; i++)
			mParams[i - 1] = array[i];
	}

	public static JPredicate parsePredicate(String line) {
		String[] params = line.split("_");
		List<String> array = new ArrayList<String>();
		for (int i = 0; i < params.length; i++)
			array.add(params[i]);
		return new JPredicate(array);
	}

	public static JPredicate parseByVar(String line,
			HashMap<String, String> mapVariableValue,
			List<JPredicate> currentState) {
		String[] params = line.split("_");
		boolean hasVar = false;
		List<String> array = new ArrayList<String>();
		array.add(params[0]);
		for (int i = 1; i < params.length; i++) {
			if (mapVariableValue.containsKey(params[i]))
				array.add(mapVariableValue.get(params[i]));
			else if (params[i].charAt(0) == '$') {
				if (!hasVar)
					hasVar = true;
				array.add(params[i]);
			} else {
				System.err.println("paseByVar found error var:" + line);
				assert (false);
			}
		}
		JPredicate result = new JPredicate(array);
		if (!hasVar)
			return result;
		else {
			for (JPredicate pre : currentState) {
				if (pre.equalIgnoreVar(result)) {
					for (int i = 0; i < result.mParams.length; i++) {
						if (result.mParams[i].toString().charAt(0) == '$') {
							mapVariableValue.put(result.mParams[i].toString(),
									pre.mParams[i].toString());
						}
					}
					return pre;
				}
			}
			System.err.println("parsebyvar error:" + result);
			assert (false);
			return null;
		}
	}

	@Override
	public String toString() {
		String msg = this.mName;
		for (Object obj : this.mParams)
			msg += obj.toString();
		return msg;
	}

	@Override
	public boolean equals(Object obj) {
		JPredicate pre = (JPredicate) obj;
		if (pre == null)
			return false;
		else {
			boolean flag = this.mName.equalsIgnoreCase(pre.mName);
			if (flag && this.mName.equals("near")
					&& this.mParams[0].equals(pre.mParams[1])
					&& this.mParams[1].equals(pre.mParams[0])) {
				return true;
			}

			for (Object pro : this.mParams) {
				if (!flag)
					break;
				boolean subflag = false;
				for (Object pro2 : pre.mParams) {
					if (pro.equals(pro2)) {
						subflag = true;
						break;
					}
				}
				flag = subflag;
			}
			return flag;
		}
	}

	private boolean equalIgnoreVar(JPredicate pre) {
		if (!this.mName.equals(pre.mName)) {
			return false;
		}
		boolean result = true;
		for (int i = 0; i < this.mParams.length; i++) {
			if (this.mParams[i].toString().charAt(0) == '$'
					|| pre.mParams[i].toString().charAt(0) == '$'
					|| this.mParams[i].equals(pre.mParams[i]))
				continue;
			result = false;
			break;
		}
		return result;
	}

	public static boolean contraPredicate(String line1, String line2) {
		String[] array = line1.split("_");
		JPredicate pre1 = new JPredicate(array);
		array = line2.split("_");
		JPredicate pre2 = new JPredicate(array);
		return pre1.contraPredicate(pre2);
	}

	public static boolean canBeEffect(String line) {
		String name = line.split("_")[0];
		return !name.equals("near") && !name.equals("small");
	}

	/** �ж�����predicate�Ƿ���ì�� */
	public boolean contraPredicate(JPredicate pre) {
		if (this.mName.equals("holding")) { // holding��empty��ì��
			if (pre.mName.equals("empty"))
				return true;
		}
		if (this.mName.equals("empty")) { // holding��empty��ì��
			if (pre.mName.equals("holding"))
				return true;
		}
		if (this.mName.equals("location") && pre.mName.equals("location")) { // ͬһ��λ��������ͬ��location��ì��
			if (this.mParams[0].equals(pre.mParams[0])
					&& !this.mParams[1].equals(pre.mParams[1]))
				return true;
		}
		return false;
	}

	/* the distance between each predicate */
	public static int getPreDistance(JPredicate pre1, JPredicate pre2) {
		int result = 0;
		if (pre1.mName.equals(pre2.mName) && pre1.mName.equals("location")
				&& pre1.mParams[0].equals(pre2.mParams[0])) {
			int p1 = Integer.parseInt(pre1.mParams[1].toString());
			int p2 = Integer.parseInt(pre2.mParams[1].toString());
			int x = p1 % 1000 - p2 % 1000;
			int y = p1 / 1000 - p2 / 1000;
			result += Math.sqrt(x * x + y * y);
		} else if (pre1.contraPredicate(pre2))
			result += Constants.JMAX;
		return result;
	}

	/* �ж��ڵ�ǰ��״̬��ν���Ƿ���ȷ */
	public static boolean predicateCorrect(String predicateStr,
			HashMap<String, String> mapVariableValue,
			List<JPredicate> currentstate) {
		JPredicate pre = JPredicate.parseByVar(predicateStr, mapVariableValue,
				currentstate);
		for (JPredicate p : currentstate)
			if (p.equals(pre))
				return true;
		return defaultPredicateCorrect(pre);
	}

	public static boolean actionCorrent(String action, JActionModelDef model,
			List<JPredicate> currentstate) {
		List<String> variable = model.mActionVariable;
		HashMap<String, String> mapVariable = new HashMap<String, String>();
		// String[] array = this.mNextAction.split("_");//fixbug yangyang
		// 20111130
		String[] array = action.split("_");
		for (int i = 0; i < variable.size(); i++) {
			mapVariable.put(variable.get(i), array[i + 1]);
		}
		for (String str : model.mPrecondition) { // 检查是否满足当前条件
			if (!JPredicate.predicateCorrect(str, mapVariable, currentstate))
				return false;
		}
		return true;
	}

	public static boolean defaultPredicateCorrect(JPredicate pre) {
		if (pre.getName().equals("near")) {
			int l1 = Integer.parseInt(pre.getParams()[0].toString());
			int l2 = Integer.parseInt(pre.getParams()[1].toString());
			int dis = Math.abs(l1 - l2);
			return dis == 1 || dis == 1000;
		}
		return false;
	}

	public boolean containContraPredicate(List<JPredicate> list) {
		for (JPredicate pre : list) {
			if (this.contraPredicate(pre) && !this.mName.equals("position"))
				return true;
		}
		return false;
	}
}
